package com.ruoyi.socket.discard;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;

import java.util.Date;

/**
 * Handles a server-side channel.
 */
public class DiscardServerHandler extends SimpleChannelInboundHandler<Object> { // (1)


    /**
     * chanelRead() 事件处理方法。每当从客户端收到新的数据时，这个方法会在收到消息时被调用
     * 收到的消息的类型是 ByteBuf
     *
     * @param ctx
     * @param msg
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // Discard the received data silently.
        System.out.println("channelRead..." + msg);
        ByteBuf in = (ByteBuf) msg;
        try {
            while (in.isReadable()) { // (1)
                System.out.print((char) in.readByte());
            }
        } finally {
            ReferenceCountUtil.release(msg); // (2)
        }
        ctx.writeAndFlush("received ");
        // ChannelHandlerContext 对象提供了许多操作，使你能够触发各种各样的 I/O 事件和操作。
        // 这里我们调用了 write(Object) 方法来逐字地把接受到的消息写入。请注意不同于 DISCARD 的例子我们并没有释放接受到的消息，这是因为当写入的时候 Netty 已经帮我们释放了。
        //ctx.write(Object) 方法不会使消息写入到通道上，他被缓冲在了内部，你需要调用 ctx.flush() 方法来把缓冲区中数据强行输出。
        // 或者你可以用更简洁的 cxt.writeAndFlush(msg) 以达到同样的目的。
    }

    //    1.channelActive() 方法将会在连接被建立并且准备进行通信时被调用。
// 因此让我们在这个方法里完成一个代表当前时间的32位整数消息的构建工作。
    @Override
    public void channelActive(final ChannelHandlerContext ctx) {
        System.out.println("channelActive ...");
        //当连接被建立并且准备进行通信时,反写数据给当前连接
        // (2)2.为了发送一个新的消息，我们需要分配一个包含这个消息的新的缓冲。因为我们需要写入一个32位的整数，
        // 因此我们需要一个至少有4个字节的 ByteBuf。
        // 通过 ChannelHandlerContext.alloc() 得到一个当前的ByteBufAllocator，然后分配一个新的缓冲。
        final ByteBuf time = ctx.alloc().buffer(4);
        long currentTimeMillis = System.currentTimeMillis();
        time.writeInt((int) (currentTimeMillis / 1000L + 2208988800L));

        // 3.和往常一样我们需要编写一个构建好的消息。但是等一等，flip 在哪？难道我们使用 NIO 发送消息时不是调用 java.nio.ByteBuffer.flip() 吗？
        // ByteBuf 之所以没有这个方法因为有两个指针，一个对应读操作一个对应写操作。当你向 ByteBuf 里写入数据的时候写指针的索引就会增加，同时读指针的索引没有变化。
        // 读指针索引和写指针索引分别代表了消息的开始和结束。
        // 比较起来，NIO 缓冲并没有提供一种简洁的方式来计算出消息内容的开始和结尾，除非你调用 flip 方法。当你忘记调用 flip 方法而引起没有数据或者错误数据被发送时，你会陷入困境。这样的一个错误不会发生在 Netty 上，因为我们对于不同的操作类型有不同的指针。你会发现这样的使用方法会让你过程变得更加的容易，因为你已经习惯一种没有使用 flip 的方式。
        //
        //另外一个点需要注意的是 ChannelHandlerContext.write() (和 writeAndFlush() )方法会返回一个 ChannelFuture 对象，
        // 一个 ChannelFuture 代表了一个还没有发生的 I/O 操作。这意味着任何一个请求操作都不会马上被执行，因为在 Netty 里所有的操作都是异步的。

        final ChannelFuture f = ctx.writeAndFlush(time); // (3)
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                assert f == future;
                // 服务端不主动关闭通道
                //ctx.close();
            }
        });
        // (4) 4.当一个写请求已经完成是如何通知到我们？这个只需要简单地在返回的 ChannelFuture 上增加一个ChannelFutureListener。
        // 这里我们构建了一个匿名的 ChannelFutureListener 类用来在操作完成时关闭 Channel。


    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        System.out.println("exceptionCaught...");
        System.out.println(cause.getMessage());
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {

        //ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        //将未决消息冲刷到远程节点，并且关闭该Channel
        System.out.println("channelReadComplete...");
    }
}